//////////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>

#ifdef LINUX
#include <wx/imaglist.h>
#endif
#include <algorithm>
#include "rcmdapp.h"
#include "mainwnd.h"
#include "portforwardtab.h"
#include "searchpanel.h"
#include <wx/gbsizer.h>
#include <wx/regex.h>

#include "rcmdapp.h"
#include "mainwnd.h"


enum cListColumns  { cListColumnLabel=0, cListColumnProto, cListColumnFromPort, cListColumnTo, cListColumnToPort, cListColumnAuto, cListColumnEnabled  };

enum cIDS
{
	ID_LIST=8200, ID_BTN_ADD, ID_BTN_EDIT, ID_BTN_REMOVE, ID_CB_AUTO, ID_PROTO_COMBO, ID_PORT_FROM,ID_PORT_FROM_TO, ID_ENABLE_RANGE,
	ID_PORT_TO, ID_BTN_TOGLE_RULE, ID_BTN_PF_TEST, ID_BTN_RADIO_SINGLE, ID_BTN_RADIO_RANGE, ID_ADD_TO_ALLOW_LIST, ID_ADD_TO_BLOCK_LIST 
};

static SEARCHFIELD sf[]=
{
	{ cSearchTypeString,	cListColumnLabel,	_("Name")		},
	{ cSearchTypeString,	cListColumnProto,	_("Protocol")	},
	{ cSearchTypeNumeric,	cListColumnFromPort,_("From Port")	},
	{ cSearchTypeString,	cListColumnTo,		_("TO IP")		},
	{ cSearchTypeNumeric,	cListColumnToPort,	_("TO Port")	},
	{ cSearchTypeString,	cListColumnAuto,	_("Type")		},
	{ cSearchTypeString,	cListColumnEnabled,	_("Status")		},
};


BEGIN_EVENT_TABLE(CPortForwardTab, wxPanel)
	EVT_LIST_COL_CLICK(ID_LIST, CPortForwardTab::OnColumn)
	EVT_BUTTON(ID_BTN_ADD, CPortForwardTab::OnRuleAdd)
	EVT_BUTTON(ID_BTN_EDIT, CPortForwardTab::OnRuleEdit)
	EVT_BUTTON(ID_BTN_REMOVE, CPortForwardTab::OnRuleRemove)
	EVT_UPDATE_UI(ID_BTN_ADD, CPortForwardTab::OnRuleAddUpdateUI)
	EVT_UPDATE_UI(ID_BTN_EDIT, CPortForwardTab::OnRuleEditUpdateUI)
	EVT_UPDATE_UI(ID_BTN_REMOVE, CPortForwardTab::OnRuleRemoveUpdateUI)
	EVT_CHECKBOX(ID_CB_AUTO, CPortForwardTab::OnCBAuto)
	EVT_BUTTON(ID_BTN_TOGLE_RULE, CPortForwardTab::OnToggle)
	EVT_UPDATE_UI(ID_BTN_TOGLE_RULE, CPortForwardTab::OnRuleToggleUpdateUI)
	EVT_BUTTON(ID_BTN_PF_TEST, CPortForwardTab::OnPortTest)
	EVT_UPDATE_UI(ID_BTN_PF_TEST, CPortForwardTab::OnPortForwardingUpdateUI)
END_EVENT_TABLE()


CPortForwardTab::CPortForwardTab(wxWindow* pParent):
	wxPanel(pParent, wxID_ANY), m_bSortAsc(true), CSearchImpl<CPortForwardTab>(*this)
{
	wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);

	m_pSearch = new CSearchPanel(this, this);
	sizer->Add(m_pSearch, 0, wxALL|wxEXPAND);

	m_nSortCol= cListColumnLabel;

	m_pList = new CLogCtrl<CPortForwardTab>(this, ID_LIST);

	SetSearchListPtr(m_pList);

	sizer->Add(m_pList, 1, wxALL|wxEXPAND);

	for (size_t i=0;i<sizeof(sf)/sizeof(sf[0]);i++)
	{
		m_pList->InsertColumn( sf[i].iFieldID, sf[i].strLabel );
		m_pList->SetColumnWidth( sf[i].iFieldID, 100  );
	}

	m_pList->SetColumnWidth( cListColumnLabel, 150  );

	wxBoxSizer* sizerBottom = new wxBoxSizer(wxHORIZONTAL);

	wxBoxSizer* sizerLft = new wxBoxSizer(wxHORIZONTAL);

	wxButton* pAdd		= new wxButton(this, ID_BTN_ADD,		_("Add"), wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxBU_EXACTFIT);
	wxButton* pEdit		= new wxButton(this, ID_BTN_EDIT,		_("Edit"),wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxBU_EXACTFIT);
	wxButton* pRemove	= new wxButton(this, ID_BTN_REMOVE,		_("Remove"),wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxBU_EXACTFIT);
	wxButton* pEnable	= new wxButton(this, ID_BTN_TOGLE_RULE, _("Enable"),wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxBU_EXACTFIT);
	wxButton* pTest		= new wxButton(this, ID_BTN_PF_TEST,	_("Test Port Forwarding"),wxDefaultPosition, wxDefaultSize, wxNO_BORDER|wxBU_EXACTFIT);

	sizerLft->Add(pAdd,			0, wxALL, 5);
	sizerLft->Add(pEdit,		0, wxALL, 5);
	sizerLft->Add(pRemove,		0, wxALL, 5);
	sizerLft->AddSpacer(10);
	sizerLft->Add(pEnable,		0, wxALL, 5);
	sizerLft->AddSpacer(10);
	sizerLft->Add(pTest,		0, wxALL, 5);


	sizerBottom->Add(sizerLft,	0, wxALL|wxALIGN_LEFT, 5); 

	sizerBottom->AddStretchSpacer();

	wxBoxSizer* sizerRght = new wxBoxSizer(wxHORIZONTAL);
	m_pAuto	= new wxCheckBox(this, ID_CB_AUTO, _("Automatic TCP Port Forwarding"));
	sizerRght->Add(m_pAuto,		0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
	sizerBottom->Add(sizerRght, 0, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5); 

	sizer->Add(sizerBottom, 0, wxALL|wxEXPAND, 5);

	SetSizer(sizer);

	m_attrRed.SetBackgroundColour( wxColor(255,0,0) );


	m_pImageList = new wxImageList(16, 16);

	wxImage icnTmp;
	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("red_led.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("yellow_led.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	if ( icnTmp.LoadFile(wxGetApp().GetImageDir()+wxT("green_led.png"),   wxBITMAP_TYPE_PNG) )
	{
		m_pImageList->Add( icnTmp );
	}

	m_pList->SetImageList(m_pImageList, wxIMAGE_LIST_SMALL);
	m_pList->SetImageList(m_pImageList, wxIMAGE_LIST_STATE);
}

CPortForwardTab::~CPortForwardTab(void)
{
}

void CPortForwardTab::LoadData()
{
	m_pAuto->SetValue( wxGetApp().GetMainWnd()->GetLogic().GetAutoPortForward() );
	m_entriesBeforeFilter = wxGetApp().GetMainWnd()->GetLogic().GetPortForward();

	ProcessData();
}

////////////////////////////////////////////////////////////////////////////
//ISearchProvider
void CPortForwardTab::GetSearchFields(SEARCHFIELD*& sfRes, size_t& sz)
{
	sfRes = sf;
	sz = sizeof(sf)/sizeof(sf[0]);
}

void CPortForwardTab::OnFilterChanged()
{
	ProcessData();
}

///////////////////////////////////////////////////////////////////////////
//IGUIBind
void CPortForwardTab::SetVal(unsigned int nVal)
{
	LoadData();
}

///////////////////////////////////////////////////////////////////////////
//Data
void CPortForwardTab::Reset()
{
	ProcessData();
	((CLogCtrl<CPortForwardTab>*)m_pList)->DataChanged(true);
}

void CPortForwardTab::ProcessData()
{
	try
	{
		ResetSearchPos();

		DoFilter();

		DoSort();

		((CLogCtrl<CPortForwardTab>*)m_pList)->DataChanged();
	}
	catch(...)
	{
	}
}

void CPortForwardTab::DoFilter()
{
	wxString strVal;

	m_storage.erase( m_storage.begin(), m_storage.end() );

	for(size_t i=0;i<m_entriesBeforeFilter.size();i++)
	{
		bool bValid(true);

		for (size_t j=0;j<sizeof(sf)/sizeof(sf[0]);j++)
		{
			strVal = FieldToString( sf[j].iFieldID, m_entriesBeforeFilter[i] );

			if ( !m_pSearch->Match(sf[j], strVal) )
			{
				bValid = false;
				break;
			}
		}

		if ( bValid )
		{
			m_storage.push_back( m_entriesBeforeFilter[i] );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
//Sort
bool CPortForwardTab::CSortHelper::operator() (const PORTFORWARDRULE& a, const PORTFORWARDRULE& b)
{
	bool bRes(false);

	switch (m_col)
	{
		case cListColumnLabel:
			bRes = a.strLabel<b.strLabel;
		break;

		case cListColumnProto:
			bRes = a.proto<b.proto;
		break;

		case cListColumnFromPort:
			bRes = a.wPortFrom<b.wPortFrom;
		break;

		case cListColumnTo:
			bRes = a.strDst<b.strDst;
		break;

		case cListColumnToPort:
			bRes = a.wPortTo<b.wPortTo;
		break;

		case cListColumnAuto:
			bRes = a.bAutoRule<b.bAutoRule;
		break;

		case cListColumnEnabled:
			bRes = a.bEnabled<b.bEnabled;
		break;
	}

	return m_bSortAsc?bRes:!bRes;
}

void CPortForwardTab::DoSort()
{
	CSortHelper sorter(m_nSortCol, m_bSortAsc);
	std::sort( m_storage.begin(), m_storage.end(), sorter);
}

void CPortForwardTab::OnColumn(wxListEvent& ev)
{
	m_bSortAsc=!m_bSortAsc;

	m_nSortCol = ev.m_col;

	DoSort();
	
	((CLogCtrl<CPortForwardTab>*)m_pList)->DataChanged();
}


wxString CPortForwardTab::GetItemText(long item, long column, wxWindowID id)
{
	PORTFORWARDRULE& entry = m_storage[item];

	return FieldToString(column, entry);
}

//enum cListColumns  { cListColumnLabel=0, cListColumnProto, cListColumnFromPort, cListColumnTo, cListColumnToPort, cListColumnAuto };
wxString CPortForwardTab::FieldToString( int column, const PORTFORWARDRULE& entry)
{
	wxString str;

	try
	{
		switch(column)
		{
			case cListColumnLabel:
				str = entry.strLabel;
			break;

			case cListColumnProto:
				str = FormatProto(entry.proto);
			break;

			case cListColumnFromPort:
				str = wxString::Format(wxT("%u"), entry.wPortFrom);
			break;

			case cListColumnTo:
				str = entry.strDst;
			break;

			case cListColumnToPort:
				str = wxString::Format(wxT("%u"), entry.wPortTo);
			break;

			case cListColumnAuto:
				str = entry.bAutoRule?_("Auto"):_("Manual");
			break;

			case cListColumnEnabled:
				if ( entry.bRuleInProgress  )
				{
					str = _("In Progress");
				}
				else
				{
					str = entry.bEnabled?_("Enabled"):_("Disabled");
				}
			break;
		}
	}
	catch (...)
	{

	}

	return str;
}

int CPortForwardTab::GetItemColumnImage(long item, long column, wxWindowID id/*=wxID_ANY*/)
{
	int nRes(-1);

	if ( 0==column )
	{
		PORTFORWARDRULE& entry = m_storage[item];

		if ( entry.bRuleInProgress )
		{
			nRes = 1;
		}
		else
		{
			if (entry.bEnabled )
			{
				nRes = 2;
			}
			else
			{
				nRes = 0;
			}
		}
	}

	return nRes;
}


wxListItemAttr* CPortForwardTab::GetItemAttr(long item, wxWindowID id)
{
	try
	{
		PORTFORWARDRULE& entry = m_storage[item];

		if ( entry.bAutoMustBeDeleted && entry.bAutoRule )
		{
			return &m_attrRed;
		}
	}
	catch (...)
	{
	}

	return CSearchImpl<CPortForwardTab>::GetItemAttr( item, id );
}


//enum cPortFrorwardProto { cPortFrorwardProtoAny=0, cPortFrorwardProtoTCPIP,cPortFrorwardProtoUDP };
wxString CPortForwardTab::FormatProto(cPortFrorwardProto proto)
{
	wxString str;

	switch(proto)
	{
		case cPortFrorwardProtoAny:
			str = _("Any");
		break;

		case cPortFrorwardProtoTCPIP:
			str = _("TCP/IP");
		break;

		case cPortFrorwardProtoUDP:
			str = _("UDP");
		break;
	}

	return str;
}

bool CPortForwardTab::ValidateRule(const PORTFORWARDRULE& rule, bool bNew)
{
	bool bRes(true);

	for(size_t i=0;i<m_entriesBeforeFilter.size();i++)
	{
		const PORTFORWARDRULE& ruleDB = m_entriesBeforeFilter[i];

		if ( bNew && ruleDB.strLabel == rule.strLabel )
		{
			wxMessageBox(_("Name already exist!!"), _("Error"),
		                         wxICON_WARNING | wxOK, this);

			bRes = false;
			break;
		}

		if ( ruleDB.strLabel == rule.strLabel )
			continue;


		if ( ruleDB.wPortFrom == rule.wPortFrom &&
			 ruleDB.proto == rule.proto )
		{
			wxString strErr = _("Port forwarding for this source port already exist!! See rule:");
			strErr+=wxT(" ");
			strErr+=ruleDB.strLabel;

			wxMessageBox(strErr, _("Error"), wxICON_WARNING | wxOK, this);

			bRes = false;
			break;
		}

		if ( ruleDB.wPortTo == rule.wPortTo &&
			 ruleDB.proto == rule.proto &&
			 ruleDB.strDst == rule.strDst )
		{
			wxString strErr = _("Port forwarding to this port already exist!! See rule:");
			strErr+=wxT(" ");
			strErr+=ruleDB.strLabel;

			wxMessageBox(strErr, _("Error"), wxICON_WARNING | wxOK, this);

			bRes = false;
			break;
		}
	}
	
	return bRes;
}

void CPortForwardTab::OnRuleAdd(wxCommandEvent& ev)
{
	PORTFORWARDRULE rule;
	rule.strDst = wxGetApp().GetMainWnd()->GetLogic().GetCurIP();

	CPortForwardRuleEditDlg dlg(this, rule, m_entriesBeforeFilter);

	do
	{
		if ( wxID_OK != dlg.ShowModal() )
			break;

		dlg.TransferData();

		if ( ValidateRule(rule) )
		{
			m_entriesBeforeFilter.push_back(rule);

			wxGetApp().GetMainWnd()->GetLogic().ProcessPortForwarding(m_entriesBeforeFilter);

			ProcessData();
			break;
		}

	}while(true);
}

void CPortForwardTab::OnRuleEdit(wxCommandEvent& ev)
{
	long item = -1;
	item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

	try
	{
		PORTFORWARDRULE rule = m_storage[item];
		CPortForwardRuleEditDlg dlg(this, rule, m_entriesBeforeFilter, false);

		do
		{
			if ( wxID_OK != dlg.ShowModal() )
				break;

			dlg.TransferData();

			if ( ValidateRule(rule, false) )
			{
				for(size_t i=0;i<m_entriesBeforeFilter.size();i++)
				{
					PORTFORWARDRULE& ruleDB = m_entriesBeforeFilter[i];

					if ( ruleDB.strLabel == rule.strLabel )
					{
						ruleDB = rule;
						wxGetApp().GetMainWnd()->GetLogic().ProcessPortForwarding(m_entriesBeforeFilter);
						break;
					}
				}

				ProcessData();
				break;
			}

		}while(true);
	}
	catch(...){}

}

void CPortForwardTab::OnRuleRemove(wxCommandEvent& ev)
{
	long item = -1;

	try
	{
		for ( ;; )
		{
			item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

			if ( item == -1 )
				break;

			PORTFORWARDRULE rule = m_storage[item];

			for(size_t i=0;i<m_entriesBeforeFilter.size();i++)
			{
				PORTFORWARDRULE& ruleDB = m_entriesBeforeFilter[i];

				if ( ruleDB.strLabel == rule.strLabel )
				{
					PORTFORWARDLIST::iterator iCur=m_entriesBeforeFilter.begin();
					std::advance(iCur,i);
					m_entriesBeforeFilter.erase(iCur);
					break;
				}
			}
		}

		wxGetApp().GetMainWnd()->GetLogic().ProcessPortForwarding(m_entriesBeforeFilter);

		ProcessData();

		((CLogCtrl<CPortForwardTab>*)m_pList)->DataChanged(true);

	}
	catch(...) {}
}

void CPortForwardTab::OnRuleAddUpdateUI(wxUpdateUIEvent& ev)
{
	ev.Enable(true);
}

void CPortForwardTab::OnRuleEditUpdateUI(wxUpdateUIEvent& ev)
{
	long item = -1;
	item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
	ev.Enable(item != -1);
}

void CPortForwardTab::OnRuleRemoveUpdateUI(wxUpdateUIEvent& ev)
{
	long item = -1;
	item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
	ev.Enable(item != -1);
}

void CPortForwardTab::OnCBAuto(wxCommandEvent& ev)
{
	wxGetApp().GetMainWnd()->GetLogic().ProcessAutoPortForwarding( m_pAuto->GetValue() );
}

void CPortForwardTab::OnToggle(wxCommandEvent& ev)
{
	long item = -1;

	try
	{
		for ( ;; )
		{
			item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

			if ( item == -1 )
				break;

			PORTFORWARDRULE rule = m_storage[item];
			wxGetApp().GetMainWnd()->GetLogic().ToglePFRule(rule.strLabel);

			/*
			for(size_t i=0;i<m_entriesBeforeFilter.size();i++)
			{
				PORTFORWARDRULE& ruleDB = m_entriesBeforeFilter[i];

				if ( ruleDB.strLabel == rule.strLabel )
				{


					ruleDB.bAutoRule = false;
					ruleDB.bEnabled = !rule.bEnabled;
					break;
				}
			}
			*/
		}

		//wxGetApp().GetMainWnd()->GetLogic().ProcessPortForwarding(m_entriesBeforeFilter);
		//ProcessData();
		//((CLogCtrl<CPortForwardTab>*)m_pList)->DataChanged(true);

		LoadData();
		wxGetApp().GetMainWnd()->GetLogic().ExecutePortForward();
	}
	catch(...) {}
}

void CPortForwardTab::OnPortTest(wxCommandEvent& ev)
{
	long item = -1;

	try
	{
		for ( ;; )
		{
			item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

			if ( item == -1 )
				break;

			PORTFORWARDRULE entry = m_storage[item];

			wxString strPort = wxString::Format(wxT("%d"), entry.wPortFrom);
			wxString strURL = wxT("http://www.routercommander.com/toolsmenu/pftest?port=");
			strURL+=strPort;

			wxLaunchDefaultBrowser(strURL);
		}
	}
	catch(...) {}
}

void CPortForwardTab::OnRuleToggleUpdateUI(wxUpdateUIEvent& ev)
{
	long item = -1;
	item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

	if ( -1!=item )
	{
		PORTFORWARDRULE& entry = m_storage[item];
		ev.SetText( entry.bEnabled?_("Disable"):_("Enable") );
		ev.Enable( true );
	}
	else
	{
		ev.Enable( false );
	}
}

void CPortForwardTab::OnPortForwardingUpdateUI(wxUpdateUIEvent& ev)
{
	bool bEnable(false);

	long item = -1;

	try
	{
		for ( ;; )
		{
			item = m_pList->GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

			if ( item == -1 )
				break;

			PORTFORWARDRULE entry = m_storage[item];
			bEnable|= (entry.proto==cPortFrorwardProtoAny) | (entry.proto==cPortFrorwardProtoTCPIP);

			if ( entry.proto == cPortFrorwardProtoUDP )
			{
				bEnable = false;
				break;
			}
		}
	}catch(...) {}

	ev.Enable( bEnable );
}

void CPortForwardTab::OnContextMenu(long id, long col)
{
	wxMenu mnu;
	mnu.SetClientData( (void*)id );
	mnu.Append(ID_ADD_TO_ALLOW_LIST, 	_("Add program to Allow list") );
	mnu.Append(ID_ADD_TO_BLOCK_LIST, 	_("Add program to Block list") );
	mnu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CPortForwardTab::OnPopupClick), NULL, this);
	PopupMenu(&mnu);
}

void CPortForwardTab::OnPopupClick(wxCommandEvent &evt)
{
	try
	{
		long item = -1;
		for ( ;; )
		{
			item = m_pList->GetNextItem(item,
										 wxLIST_NEXT_ALL,
										 wxLIST_STATE_SELECTED);
			if ( item == -1 )
				break;

			PORTFORWARDRULE entry = m_storage[item];

			switch(evt.GetId())
			{
				case ID_ADD_TO_ALLOW_LIST:
				{
					TEXTLIST lst = wxGetApp().GetMainWnd()->GetPropStorage().GetAllowPFPolicy();
					lst.push_back( (const char*)entry.strProcName.ToUTF8() );
					wxGetApp().GetMainWnd()->GetPropStorage().SetAllowPFPolicy(lst);
				}
				break;

				case ID_ADD_TO_BLOCK_LIST:
				{
					TEXTLIST lst = wxGetApp().GetMainWnd()->GetPropStorage().GetBlockPFPolicy();
					lst.push_back( (const char*)entry.strProcName.ToUTF8() );
					wxGetApp().GetMainWnd()->GetPropStorage().SetBlockPFPolicy(lst);
				}
				break;
			}
		}
	}catch(...) {}

	wxGetApp().GetMainWnd()->GetLogic().ProcessPortForwarding(m_entriesBeforeFilter);
}



////////////////////////////////////////////////////////////////////////////////
//CPortForwardRuleEditDlg
BEGIN_EVENT_TABLE(CPortForwardRuleEditDlg, wxDialog)
	EVT_UPDATE_UI(wxID_OK, CPortForwardRuleEditDlg::OnOKUpdateUI)
	//EVT_RADIOBOX(ID_BTN_RADIO_SINGLE, CPortForwardRuleEditDlg::OnPortRange )
	//EVT_RADIOBOX(ID_BTN_RADIO_RANGE,  CPortForwardRuleEditDlg::OnPortRange )
	EVT_COMMAND_RANGE(ID_BTN_RADIO_SINGLE, ID_BTN_RADIO_RANGE,wxEVT_COMMAND_RADIOBUTTON_SELECTED,  CPortForwardRuleEditDlg::OnPortRange )
END_EVENT_TABLE()

CPortForwardRuleEditDlg::CPortForwardRuleEditDlg(wxWindow* parent, PORTFORWARDRULE& rule, const PORTFORWARDLIST& list, bool bAdd):
	wxDialog(parent, wxID_ANY, bAdd?_("New Port Forwarding Rule"):_("Edit Port Forwarding Rule"), wxDefaultPosition),
	m_rule(rule), m_list(list), m_bAdd( bAdd )
{
	wxBoxSizer* pBoxSizer = new wxBoxSizer(wxVERTICAL);

	wxGridBagSizer *pGrid = new wxGridBagSizer(10, 10);
	int nFlags=wxALL|wxALIGN_CENTER|wxEXPAND;
	int nRowCount=0;
	wxString strTMP;

	wxGBSpan span(1,3);

	pGrid->Add(new wxStaticText(this, wxID_ANY, _("Name:")), 	    wxGBPosition(nRowCount,0), wxDefaultSpan, 0);
	m_pName = new wxTextCtrl(this, wxID_ANY, rule.strLabel, wxDefaultPosition, wxDefaultSize, bAdd?0:wxTE_READONLY);
	pGrid->Add(m_pName, wxGBPosition(nRowCount,1), span, 0);
	
	nRowCount++;
	pGrid->Add(new wxStaticText(this, wxID_ANY, _("Protocol:")), 	wxGBPosition(nRowCount,0), wxDefaultSpan, 0);
	m_pProtoCombo = new wxComboBox( this, ID_PROTO_COMBO, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN|wxCB_READONLY  );
	m_pProtoCombo->Append(_("Any"),		(void*)cPortFrorwardProtoAny);
	m_pProtoCombo->Append(_("TCP/IP"),	(void*)cPortFrorwardProtoTCPIP);
	m_pProtoCombo->Append(_("UDP"),		(void*)cPortFrorwardProtoUDP);
	m_pProtoCombo->SetSelection(rule.proto);
	pGrid->Add(m_pProtoCombo, wxGBPosition(nRowCount,1), span, 0);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	m_pPortRangeSizer = new wxBoxSizer(wxHORIZONTAL);

	nRowCount++;
	pGrid->Add(new wxStaticText(this, wxID_ANY, _("From Port:")), 	wxGBPosition(nRowCount,0), wxDefaultSpan, 0);

	strTMP = rule.wPortFrom?wxString::Format(wxT("%u"),rule.wPortFrom):wxT("");
	m_strFromPort = strTMP;
	m_pFromPort = new wxTextCtrl(this, ID_PORT_FROM, strTMP,
		wxDefaultPosition, wxSize(60,20), 0, wxTextValidator(wxFILTER_NUMERIC, &m_strFromPort));

	m_pPortRangeSizer->Add( m_pFromPort );

	//To
	m_plblTo = new wxStaticText(this, wxID_ANY, _("-"));
	m_pPortRangeSizer->Add( m_plblTo );

	strTMP = rule.wPortFrom?wxString::Format(wxT("%u"),rule.wPortFromTo):wxT("");
	m_strFromPortTo = strTMP;
	m_pFromPortTo = new wxTextCtrl(this, ID_PORT_FROM, strTMP,
		wxDefaultPosition, wxSize(60,20), 0, wxTextValidator(wxFILTER_NUMERIC, &m_strFromPortTo));

	m_pPortRangeSizer->Add( m_pFromPortTo );
	pGrid->Add(m_pPortRangeSizer, 	wxGBPosition(nRowCount,1), wxDefaultSpan, 0);

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	nRowCount++;
	m_prdioSingle = new wxRadioButton(this, ID_BTN_RADIO_SINGLE, _("single"));
	pGrid->Add(m_prdioSingle,wxGBPosition(nRowCount,0), wxDefaultSpan, wxLEFT, 10);
	m_prdioSingle->SetValue( rule.wPortFrom == rule.wPortFromTo );

	m_prdioRange = new wxRadioButton(this, ID_BTN_RADIO_RANGE, _("range"));
	pGrid->Add(m_prdioRange, wxGBPosition(nRowCount,1), wxDefaultSpan, 0);
	m_prdioRange->SetValue( rule.wPortFrom != rule.wPortFromTo );
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	nRowCount++;
	pGrid->Add(new wxStaticText(this, wxID_ANY, _("To IP:")), 		wxGBPosition(nRowCount,0), wxDefaultSpan, 0);
	m_pIP = new wxTextCtrl(this, wxID_ANY, rule.strDst  );
	pGrid->Add(m_pIP, wxGBPosition(nRowCount,1), span, 0);

	nRowCount++;
	pGrid->Add(new wxStaticText(this, wxID_ANY, _("To Port:")), 	wxGBPosition(nRowCount,0), wxDefaultSpan, 0);
	strTMP = rule.wPortTo?wxString::Format(wxT("%u"),rule.wPortTo):wxT("");
	m_strToPort = strTMP;
	m_pToPort = new wxTextCtrl(this, ID_PORT_TO, strTMP,
		wxDefaultPosition, wxDefaultSize, 0, wxTextValidator(wxFILTER_NUMERIC, &m_strToPort));

	pGrid->Add(m_pToPort, wxGBPosition(nRowCount,1), span, 0);

	nRowCount++;
	wxSizer* sz = CreateStdDialogButtonSizer(wxOK|wxCANCEL);
	pGrid->Add(sz, 		wxGBPosition(nRowCount,0), wxGBSpan(1,2), nFlags);

	pBoxSizer->Add(pGrid, 1, wxALL|wxALIGN_CENTER|wxEXPAND, 20);

	SetSizer(pBoxSizer);
	pBoxSizer->Fit(this);

	wxCommandEvent evt;
	OnPortRange(evt);
}

void CPortForwardRuleEditDlg::TransferData()
{
	m_rule.strLabel = m_pName->GetValue();
	m_rule.strDst = m_pIP->GetValue();

	long l=0;
	m_pFromPort->GetValue().ToLong(&l);
	m_rule.wPortFrom = (unsigned short)l;

	m_rule.wPortFromTo = m_rule.wPortFrom;

	if ( m_prdioRange->GetValue() )
	{
		m_pFromPortTo->GetValue().ToLong(&l);
		m_rule.wPortFromTo = (unsigned short)l;

		if ( m_rule.wPortFrom>m_rule.wPortFromTo)
		{
			m_rule.wPortFromTo = m_rule.wPortFrom;
		}
	}

	m_pToPort->GetValue().ToLong(&l);
	m_rule.wPortTo = (unsigned short)l;

	int nSel = m_pProtoCombo->GetSelection();

	if ( wxNOT_FOUND!=nSel )
	{
		void* p = m_pProtoCombo->GetClientData(nSel);
		m_rule.proto = (cPortFrorwardProto)(int)p;
	}
}

void CPortForwardRuleEditDlg::OnOKUpdateUI(wxUpdateUIEvent& ev)
{
	bool bEnable(true);
	long l=0;

	bEnable&= !m_pName->GetValue().empty();
	
	bEnable&= m_pFromPort->GetValue().ToLong(&l);
	bEnable&= l>0 && l<=0xFFFF;

	bEnable&= m_pToPort->GetValue().ToLong(&l);
	bEnable&= l>0 && l<=0xFFFF;

	//TODO: have a bug, last digit was not validated
	wxString strIP = m_pIP->GetValue();
	wxRegEx regexp(wxT("^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"), wxRE_DEFAULT);
	
	bEnable&=regexp.Matches(strIP);

	ev.Enable( bEnable );
}

void CPortForwardRuleEditDlg::OnPortRange(wxCommandEvent& evt)
{
	bool bSingle = m_prdioSingle->GetValue();

	m_pPortRangeSizer->Show( m_plblTo, !bSingle );
	m_pPortRangeSizer->Show( m_pFromPortTo, !bSingle );
}

